package com.kuaimacode.kframework.api.jwt.controller;

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.kuaimacode.kframework.api.jwt.JwtTokenUtil;
import com.kuaimacode.kframework.api.jwt.JwtUser;
import com.kuaimacode.kframework.api.jwt.JwtUserFactory;
import com.kuaimacode.kframework.api.jwt.service.JwtAuthenticationResponse;
import com.kuaimacode.kframework.api.service.UserService;
import com.kuaimacode.kframework.api.util.MaskUtil;
import com.kuaimacode.kframework.api.vo.request.JwtAuthReq;
import com.kuaimacode.kframework.api.vo.response.BaseLoginResp;
import com.kuaimacode.kframework.common.JsonResult;
import com.kuaimacode.kframework.exception.ErrorEnum;
import com.kuaimacode.kframework.exception.ServerInternalErrorException;
import com.kuaimacode.kframework.mp.JsonUtils;
import com.kuaimacode.kframework.mybatis.models.user.User;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Objects;

@RestController
public class AuthenticationRestController {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationRestController.class);
    @Value("${jwt.header}")
    private String tokenHeader;
    @Value("${aliyuns.api.domain}")
    private String aliyunsDomain;
    @Value("${aliyuns.api.keyid}")
    private String aliyunsKey;
    @Value("${aliyuns.api.secret}")
    private String aliyunsSecret;
    @Value("${spring.profiles.active}")
    private String enviroment;

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    @Qualifier("jwtUserDetailsService")
    private UserDetailsService userDetailsService;
    @Autowired
    private UserService userService;

    @ApiOperation(value = "登陆时校验AccessCode", notes = "登陆时校验AccessCode")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "AccessCode", value = "阿里返回的AccessCode", required = true, dataType = "string", paramType = "path"),
            @ApiImplicitParam(name = "PhoneNumber", value = "手机号", required = true, dataType = "string", paramType = "path")
    })
    @RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
    public JsonResult createAuthenticationToken(@RequestBody JwtAuthReq authReq) throws AuthenticationException {
        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", aliyunsKey, aliyunsSecret);
        IAcsClient client = new DefaultAcsClient(profile);

        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain(aliyunsDomain);
        request.setProtocol(ProtocolType.HTTPS);
        request.setVersion("2017-05-25");
        request.setAction("VerifyMobile");
        request.putQueryParameter("AccessCode", authReq.getAccessCode());
        request.putQueryParameter("PhoneNumber", authReq.getPhoneNumber());
        try {
            LOGGER.info("login request AccessCode: {}, PhoneNumber: {}", authReq.getAccessCode(), authReq.getPhoneNumber());
            CommonResponse response;
            if ("dev".equals(enviroment)) {
                response = new CommonResponse();
                response.setData("{\"Code\":\"OK\", \"GateVerifyResultDTO\":{\"VerifyResult\":\"PASS\"}}");
            } else {
                response = client.getCommonResponse(request);
            }
            JSONObject jsonObject = JsonUtils.parseJson(response.getData());
            LOGGER.info("login response: {}, AccessCode: {}, PhoneNumber: {}", jsonObject.toJSONString(), authReq.getAccessCode(), authReq.getPhoneNumber());
            if ("OK".equals(jsonObject.getString("Code")) && "PASS".equals(jsonObject.getJSONObject("GateVerifyResultDTO").getString("VerifyResult"))) {

                User user = userService.selectByPhone(authReq.getPhoneNumber());
                if (user == null) {
                    user = createUser(authReq.getPhoneNumber());
                }

                final String token = jwtTokenUtil.generateToken(JwtUserFactory.create(user, new ArrayList<>()));
                BaseLoginResp loginResp = new BaseLoginResp();
                loginResp.setIsProtocol(user.getIsProtocol());
                loginResp.setToken(token);
                //登录成功后修改用户token
                userService.updateUserToken(user.getId(), token);
                return JsonResult.success(loginResp);
            }
        } catch (ClientException e) {
            LOGGER.error(e.getMessage(), e);
        }
        throw new ServerInternalErrorException(ErrorEnum.ACCESS_CODE_VALIDATE_ERROR);
    }

    /**
     * 创建用户
     *
     * @param phoneNumber
     * @return
     */
    private User createUser(String phoneNumber) {
        User user = new User();
        user.setUsername(phoneNumber);
        user.setNickname("用户" + MaskUtil.interceptBack4(phoneNumber));
        user.setPhone(phoneNumber);
        user.setEnabled(1);
        user.setIsProtocol((short) 1);
        userService.add(user);
        return user;
    }

    @RequestMapping(value = "${jwt.route.authentication.refresh}", method = RequestMethod.GET)
    public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest request) {
        String authToken = request.getHeader(tokenHeader);
        final String token = authToken.substring(7);
        String username = jwtTokenUtil.getUsernameFromToken(token);
        JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);

        if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())) {
            String refreshedToken = jwtTokenUtil.refreshToken(token);
            return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken));
        } else {
            return ResponseEntity.badRequest().body(null);
        }
    }

    @ExceptionHandler({AuthenticationException.class})
    public ResponseEntity<String> handleAuthenticationException(AuthenticationException e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
    }

    /**
     * Authenticates the user. If something is wrong, an {@link AuthenticationException} will be thrown
     */
    private void authenticate(String username, String password) {
        Objects.requireNonNull(username);
        Objects.requireNonNull(password);

        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e) {
            throw new AuthenticationException("User is disabled!", e);
        } catch (BadCredentialsException e) {
            throw new AuthenticationException("Bad credentials!", e);
        }
    }
}
